package org.taobao.ibatiside.ui.view;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.model.BaseWorkbenchContentProvider;
import org.eclipse.ui.navigator.CommonViewer;
import org.eclipse.ui.navigator.IMementoAware;
import org.taobao.ibatiside.core.IbatisConstant;
import org.taobao.ibatiside.core.filemodel.FileProber;
import org.taobao.ibatiside.core.filemodel.IbatisElement;
import org.taobao.ibatiside.core.filemodel.IbatisProject;

public class IbatisModelContentProvider extends BaseWorkbenchContentProvider
		implements IMementoAware, IResourceChangeListener {
	private CommonViewer batisViewer;
	public static final String TAG_PATH="resource";
	public static final String ATTR_NAME="path";
	private BatisCommonViewerMapper viewerMapper = new BatisCommonViewerMapper();

	@Override
	public Object[] getChildren(Object element) {
		if (element instanceof IWorkspaceRoot) {
			return getIbatisProject((IWorkspaceRoot) element);
		}
		return super.getChildren(element);
	}

	@Override
	public void dispose() {
		if (batisViewer != null) {
			batisViewer.setMapper(null);
			IWorkspace workspace = null;
			Object obj = batisViewer.getInput();
			if (obj instanceof IWorkspace) {
				workspace = (IWorkspace) obj;
			} else if (obj instanceof IContainer) {
				workspace = ((IContainer) obj).getWorkspace();
			}
			if (workspace != null) {
				workspace.removeResourceChangeListener(this);
			}
		}

		super.dispose();
	}

	
	@Override
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		super.inputChanged(viewer, oldInput, newInput);
		this.batisViewer = (CommonViewer) viewer;
		this.batisViewer.setMapper(viewerMapper);
		this.batisViewer.setLabelProvider(new DecoratingLabelProvider(new BatisNavigatorLabelProvider(), PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator()));
		IWorkspace oldWorkspace = null;
		IWorkspace newWorkspace = null;

		if (oldInput instanceof IWorkspace) {
			oldWorkspace = (IWorkspace) oldInput;
		} else if (oldInput instanceof IContainer) {
			oldWorkspace = ((IContainer) oldInput).getWorkspace();
		}

		if (newInput instanceof IWorkspace) {
			newWorkspace = (IWorkspace) newInput;
		} else if (newInput instanceof IContainer) {
			newWorkspace = ((IContainer) newInput).getWorkspace();
		}

		if (oldWorkspace != newWorkspace) {
			if (oldWorkspace != null) {
				oldWorkspace.removeResourceChangeListener(this);
			}
			if (newWorkspace != null) {
				newWorkspace.addResourceChangeListener(this,
						IResourceChangeEvent.POST_CHANGE);
			}
		}
	}

	private Object[] getIbatisProject(IWorkspaceRoot root) {
		IProject[] projects = root.getProjects();
		List<IbatisProject> ibatis_projects = new ArrayList<IbatisProject>();
		int project_version = -1;
		for (IProject project : projects) {
			if ((project_version = FileProber.getIbatisVerion(project)) != IbatisConstant.NOT_IBATIS) {
				ibatis_projects
						.add(new IbatisProject(project, project_version));
			}
		}
		return ibatis_projects.toArray();
	}

	@Override
	public void restoreState(IMemento aMemento) {
		IMemento[] mems = aMemento.getChildren(TAG_PATH);
		for (int i = 0; i < mems.length; i++) {
			String path = mems[i].getString(ATTR_NAME);
			IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
			Object project_wrapper = viewerMapper.getWrapper(resource);
			if (project_wrapper != null){
				batisViewer.expandToLevel(project_wrapper, 1);
			}
		}
	}

	@Override
	public void saveState(IMemento aMemento) {
		Object[] expanded = batisViewer.getVisibleExpandedElements();
		for (Object ex: expanded){
			IResource ex_resource;
			if ((ex instanceof IbatisElement) && (ex_resource = ((IbatisElement)ex).getResource()) != null){
				IMemento child_im = aMemento.createChild(TAG_PATH);
				String path = ex_resource.getFullPath().toString();
				child_im.putString(ATTR_NAME,path);
			}
		}
	}

	@Override
	public void resourceChanged(IResourceChangeEvent event) {
		Control ctrl = batisViewer.getControl();
		if (ctrl == null || ctrl.isDisposed()) {
			return;
		}
		final Collection<Runnable> runnables = new ArrayList<Runnable>();
		collectDelta(event.getDelta(), runnables);
		if (runnables.isEmpty()) {
			return;
		}
		if (ctrl.getDisplay().getThread() == Thread.currentThread()) {
			runUpdates(runnables);
		} else {
			ctrl.getDisplay().asyncExec(new Runnable() {
				public void run() {
					// Abort if this happens after disposes
					Control ctrl = batisViewer.getControl();
					if (ctrl == null || ctrl.isDisposed()) {
						return;
					}

					runUpdates(runnables);
				}
			});
		}
	}

	protected void runUpdates(Collection<Runnable> runnables) {
		Iterator runnableIterator = runnables.iterator();
		while (runnableIterator.hasNext()) {
			((Runnable) runnableIterator.next()).run();
		}
	}

	private void collectDelta(IResourceDelta delta,
			Collection<Runnable> runnables) {
		final IResource resource = delta.getResource();
		final Object mapping_element = viewerMapper.getWrapper(resource);
		if (resource instanceof IProject) {
			final IProject current_project = (IProject) resource;
			// New Project?
			if ((delta.getKind() & IResourceDelta.ADDED) != 0) {
				final int batis_ver = FileProber
						.getIbatisVerion(current_project);
				if (batis_ver != IbatisConstant.NOT_IBATIS) {
					runnables.add(new Runnable() {
						@Override
						public void run() {
							batisViewer.add(resource.getWorkspace().getRoot(),
									new IbatisProject(current_project,
											batis_ver));
						}
					});
				}
				return;
			}else if ((delta.getKind() & IResourceDelta.REMOVED) != 0){
					runnables.add(new Runnable() {
						@Override
						public void run() {
							if (mapping_element != null){
								batisViewer.remove(mapping_element);
							}
						}
					});
			}
			int changeFlags = delta.getFlags();
			if ((changeFlags & IResourceDelta.OPEN) != 0 && mapping_element != null) {
				if (resource.isAccessible())  {
					runnables.add(new Runnable() {
						@Override
						public void run() {
							batisViewer.update(mapping_element, null);							
						}
					});
				} else {
					runnables.add(new Runnable() {
						
						@Override
						public void run() {
							batisViewer.refresh(mapping_element);
						}
					});
				}
			}
		}
		IResourceDelta[] affected = delta.getAffectedChildren();
		for (int i = 0; i < affected.length; i++) {
			collectDelta(affected[i], runnables);
		}
	}
}
